Now on: joopl.Attribute
Index
1.0 What is an attribute?
Usually class definitions contain a class ctor, properties, methods and/or events, also known as class members. Class members define the information and behavior of a given class.
In some cases, classes require some descriptive information that may be useful by the consumers.
For example, a class may need to define that requires user authentication and the minimum security role to use its members is administrator.
How can an arbitrary class tell the environment "I will not work if the authenticated user is not an administrator"? The answer is *attributes.*
An attribute is an inherited class of Attribute
which defines some metadata that can be identified by other pieces and it is added to the class definition during desing-time.
Finally, a class supports as many attributes as the code requires. The attributes
parameters for the $def
operator is an array of attributes.
2.0 How to implement and consume an attribute
The so-called I will not work if the authenticated user is not an administrator attribute may be implemented as a class called RequiresAuthenticationAttribute
:
$namespace.using("joopl", "myNamespace", function(joopl, myNamespace) {
myNamespace.declareClass("RequiresAuthenticationAttribute", {
inherits: joopl.Attribute
});
});
Later on, some class that may require authentication to work will apply the whole RequiresAuthenticationAttribute
as follows:
$namespace.using("myNamespace", function(myNamespace) {
myNamespace.declareClass("MyClass", {
attributes: [new myNamespace.RequiresAuthenticationAttribute()]
});
});
Finally, some other code which instantiate the MyClass
class will inspect if the class requires authentication:
$namespace.using("myNamespace", function(myNamespace) {
if(myNamespace.MyClass.type.hasAttribute(myNamespace.RequiresAuthenticationAttribute)) {
// Do some stuff if MyClass has the whole attribute
} else {
throw Error("Sorry, this code will not execute classes if they do not require authentication...");
}
});
2.1 Attributes with parameters
Sometimes using an attribute as is is not enough, because the attribute itself should contain data.For example, some code may require some classes to define a default property. Person
class may have FirstName
, Surname
and Nickname
properties. Which one will be the one to display in some listing?
$namespace.using("joopl", "myNamespace", function(joopl, myNamespace) {
myNamespace.declareClass("DefaultPropertyAttribute", {
inherits: oopl.Attribute,
ctor: function(args) {
this._.defaultPropertyName = args.defaultPropertyName;
},
members: {
get defaultPropertyName() { return this._.defaultPropertyName; }
}
});
myNamespace.declareClass("Person", {
attributes: [new myNamespace.DefaultPropertyAttribute("nickname")],
ctor: function() {
this._.firstName = null;
this._.surname = null;
this._.nickname = null;
}
members: {
get firstName() { return this._.firstName; },
set firstName(value) { this._.firstName = value; },
get surname() { return this._.surname; },
set surname(value) { this._.surname = value; },
get nickname() { return this._.nickname; },
set nickname(value) { this._.nickname = value; }
}
});
});
Now, some code consumes instances of Person
and creates some HTML listing using standard DOM and the display name for the whole person will be taken from the DefaultPropertyValueAttribute
:
$namespace.using("myNamespace", function(myNamespace) {
// The first step is creating a regular instance of Person
var person = new myNamespace.Person();
person.firstName = "Matias";
person.surname = "Fidemraizer";
person.nickname = "mfidemraizer";
// Secondly, this is checking if the Person class has the whole attribute
if(Person.type.hasAttribute(myNamespace.DefaultPropertyAttribute)) {
// Yes, it has the attribute!
//
// Then, the attribute instance is retrieved from the type information
var defaultProperty = Person.type.getAttribute(myNamespace.DefaultPropertyAttribute);
// Once the attribute is retrieved, the code can access the "defaultPropertyName" instance property
// of the DefaultPropertyAttribute
var defaultPropertyName = defaultProperty.defaultPropertyName;
// Since any object is also an associative array (this is plain JavaScript!),
// the default property can be retrieved by using the "defaultPropertyName" variable
// as key of the array
var defaultPropertyValue = person[defaultPropertyName];
// Finally, this is creating a paragraph containing the defaultPropertyValue. In this case,
// it will be "mfidemraizer", because the Person class has the DefaultPropertyAttribute set to "nickname"!
var p = document.createElement("p");
p.appendChild(document.createTextNode(defaultPropertyValue));
document.body.appendChild(p);
}
});